package com.concurrency.example.aqs;

import com.sun.org.apache.regexp.internal.RE;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;

/**
 * 利用同步器实现一个独占锁
 * 同步器有三个主要的方法，用来获取或修改state
 * 1. getState()
 * 2. setState(release)
 * 3. boolean compareAndSetState(int expect, int update)
 *
 * 根据需求以定义内部类实现同步器，并重写它的模板方法，实现自定义功能。然后MutexLock采用代理的方式，把方法的实现交给自定义类。
 *
 * ？为什么要用代理，不直接用MutexLock实现同步器呢：因为MutexLock不是同步器，体现了面向对象的思想。
 * state状态为1表示获取锁，0表示没有线程获取锁
 */
public class MutexLock {
    private static final Integer LOCK = 1;
    private static final Integer UNLOCK = 0;
    private static class Sync extends AbstractQueuedSynchronizer {
        /**
         * 获取锁
         * @param acquire
         * @return
         */
        @Override
        protected boolean tryAcquire(int acquire) {
            if (compareAndSetState(UNLOCK, LOCK)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        /**
         * 释放锁
         * @param release
         * @return
         */
        @Override
        protected boolean tryRelease(int release) {
            if (getState() == UNLOCK) {
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(release);
            return true;
        }

        /**
         * 是否处于独占状态
         * @return
         */
        @Override
        protected boolean isHeldExclusively() {
           return getState() == LOCK;
        }

        /**
         * 返回一个Condition，每个condition都包含了一个condition队列
         * @return
         */
        Condition newCondition() {
            return new ConditionObject();
        }
    }
    /**
     * 这里采用代理模式
     */
    private final Sync sync = new Sync();

    public void lock() {
        /*该方法会调用tryAcquire*/
        sync.acquire(LOCK);
    }

    public boolean tryLock() {
       return sync.tryAcquire(LOCK);
    }

    public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException{
        return sync.tryAcquireNanos(LOCK, unit.toNanos(timeout));
    }

    public void unLock() {
        sync.tryRelease(UNLOCK);
    }
}
